#include "lxz_config.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <time.h>

#ifdef LXZAT_WIN32
#include <windows.h>
#endif /* LXZAT_WIN32 */

#ifdef LXZAT_LINUX
#include <unistd.h>
#include <dirent.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/select.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <linux/sockios.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <pthread.h>
#include <errno.h>
#endif /* LXZAT_LINUX */

#include "lxz_types.h"
#include "lxz_dstring.h"
#include "os_port.h"
#include "lxz_ppplogger.h"
#include "lxz_dbg_vmem.h"


typedef struct _lxzpcap_ppplogger_t 
{
    FILE * p_ppplogger_fd;
    pcap_filehdr_t a_pcap_filehdr;
    pcap_packethdr_t a_pcap_packethdr;
    lxz_dstring_t * pt_ppprx_buf;
    lxz_dstring_t * pt_ppptx_buf;
}lxzpcap_ppplogger_t;

static sint32 lxzpcap_ppplogger_f_flush(void * p_ppplogger_ctxt, uint08 i_io_dir, lxz_dstring_t* pt_ppp_frames);

static sint32 lxzpcap_ppplogger_f_flush(void * p_ppplogger_ctxt, uint08 i_io_dir, lxz_dstring_t* pt_ppp_frames)
{
    sint32 i_nb_flush = 0;
    sint32 i_len_frame = 0;
    lxzpcap_ppplogger_t * p_cur_ctxt = (lxzpcap_ppplogger_t *)p_ppplogger_ctxt;

    uint08 p_ll_databuf[LXZ_PPPLOGGER_MAX_LENGTH];
    i_len_frame = lxz_dstring_framelength(pt_ppp_frames, 0x7E);
    if (i_len_frame > 0)
    {
        int i = 0;
        int j = 0;
        int i_nb_read = 0;
        int i_nb_tryread = 0;
        int i_nb_trysave = 0;
        struct timeval tv;

        i_nb_tryread = i_len_frame-1;
        i_nb_read = lxz_dstring_read(pt_ppp_frames, p_ll_databuf, i_nb_tryread);
        p_ll_databuf[0] = i_io_dir;

        while (i < i_nb_read)
        {
            if (p_ll_databuf[i] != 0x7D)
            {
                p_ll_databuf[j] = p_ll_databuf[i];
            }
            else
            {
                p_ll_databuf[j] = p_ll_databuf[i+1] ^ 0x20;
                i++;
            }
            
            i++; j++;
        }

        i_nb_trysave = j+1;
        i_nb_flush = sizeof(pcap_packethdr_t) + i_nb_trysave;
        gettimeofday(&(tv), (struct timezone *) NULL);
        p_cur_ctxt->a_pcap_packethdr.it_capture_sec = tv.tv_sec;
        p_cur_ctxt->a_pcap_packethdr.it_capture_usec = tv.tv_usec;
        p_cur_ctxt->a_pcap_packethdr.it_capture_len = i_nb_trysave;
        p_cur_ctxt->a_pcap_packethdr.it_actual_len = i_nb_trysave;

        fwrite(&(p_cur_ctxt->a_pcap_packethdr), 1, sizeof(pcap_packethdr_t), p_cur_ctxt->p_ppplogger_fd);
        fwrite(&(p_ll_databuf[0]), 1, i_nb_trysave, p_cur_ctxt->p_ppplogger_fd);
        fflush(p_cur_ctxt->p_ppplogger_fd);
    }

    return i_nb_flush;
}

void * lxzpcap_ppplogger_f_open(const char * p_ppplogger_filename)
{
    lxzpcap_ppplogger_t * p_ppplogger_ctxt = NULL;
    
    p_ppplogger_ctxt = (lxzpcap_ppplogger_t *)malloc(sizeof(lxzpcap_ppplogger_t));
    if (NULL == p_ppplogger_ctxt)
    {
        return NULL;
    }
    
    memset(p_ppplogger_ctxt, 0, sizeof(lxzpcap_ppplogger_t));
    p_ppplogger_ctxt->p_ppplogger_fd = fopen(p_ppplogger_filename, "wb+");
    if (NULL == p_ppplogger_ctxt->p_ppplogger_fd)
    {
        free(p_ppplogger_ctxt);
        p_ppplogger_ctxt = NULL;
    }

    p_ppplogger_ctxt->pt_ppprx_buf = lxz_dstring_new(LXZ_PPPLOGGER_MAX_LENGTH);
    if (NULL == p_ppplogger_ctxt->pt_ppprx_buf)
    {
        fclose(p_ppplogger_ctxt->p_ppplogger_fd);

        free(p_ppplogger_ctxt);
        p_ppplogger_ctxt = NULL;
    }
    
    p_ppplogger_ctxt->pt_ppptx_buf = lxz_dstring_new(LXZ_PPPLOGGER_MAX_LENGTH);
    if (NULL == p_ppplogger_ctxt->pt_ppptx_buf)
    {
        lxz_dstring_delete(p_ppplogger_ctxt->pt_ppprx_buf);
        fclose(p_ppplogger_ctxt->p_ppplogger_fd);

        free(p_ppplogger_ctxt);
        p_ppplogger_ctxt = NULL;
    }

    p_ppplogger_ctxt->a_pcap_filehdr.ut_pcap_magic = 0xA1B2C3D4;
    p_ppplogger_ctxt->a_pcap_filehdr.ut_ver_major = 0x0002;
    p_ppplogger_ctxt->a_pcap_filehdr.ut_ver_minor = 0x0004;
    p_ppplogger_ctxt->a_pcap_filehdr.ut_cur_timezone = 0x00000000;
    p_ppplogger_ctxt->a_pcap_filehdr.ut_cur_timestamp = 0x00000000;
    p_ppplogger_ctxt->a_pcap_filehdr.ut_max_snaplength = LXZ_PPPLOGGER_MAX_LENGTH;
    p_ppplogger_ctxt->a_pcap_filehdr.ut_cur_linktype = 0x000000CC;
    fwrite(&(p_ppplogger_ctxt->a_pcap_filehdr), 1, sizeof(pcap_filehdr_t), p_ppplogger_ctxt->p_ppplogger_fd);
    fflush(p_ppplogger_ctxt->p_ppplogger_fd);

    return p_ppplogger_ctxt;
}

sint32 lxzpcap_ppplogger_f_write(void * p_ppplogger_ctxt, sint32 i_recv_flag, sint32 i_data_len, uint08 * p_data_buf)
{
    sint32 i_nb_write = 0;
    sint32 i_nb_flush = 0;

    lxzpcap_ppplogger_t * p_cur_ctxt = (lxzpcap_ppplogger_t *)p_ppplogger_ctxt;

    if (NULL != p_cur_ctxt)
    {
        if (0 == i_recv_flag)
        {
            i_nb_write = lxz_dstring_append(p_cur_ctxt->pt_ppptx_buf, p_data_buf, i_data_len);
            i_nb_flush = lxzpcap_ppplogger_f_flush(p_cur_ctxt, 0, p_cur_ctxt->pt_ppptx_buf);
        }
        else
        {
            i_nb_write = lxz_dstring_append(p_cur_ctxt->pt_ppprx_buf, p_data_buf, i_data_len);
            i_nb_flush = lxzpcap_ppplogger_f_flush(p_cur_ctxt, 1, p_cur_ctxt->pt_ppprx_buf);
        }
    }

    return i_nb_write;
}

sint32 lxzpcap_ppplogger_f_close(void * p_ppplogger_ctxt)
{
    lxzpcap_ppplogger_t * p_cur_ctxt = (lxzpcap_ppplogger_t *)p_ppplogger_ctxt;
    if (NULL != p_cur_ctxt)
    {
        lxz_dstring_delete(p_cur_ctxt->pt_ppprx_buf);
        lxz_dstring_delete(p_cur_ctxt->pt_ppptx_buf);
        fclose(p_cur_ctxt->p_ppplogger_fd);

        free(p_cur_ctxt);
        return 1;
    }
    
    return 0;
}